package com.wz.service.impl;

import com.wz.config.Normal;
import com.wz.config.USDTapi;
import com.wz.dao.TransactionDao;
import com.wz.pojo.Transaction;
import com.wz.pojo.USDT;
import com.wz.service.BlockService;
import com.wz.service.TransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.abi.EventEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Event;
import org.web3j.abi.datatypes.generated.Bytes32;
import org.web3j.abi.datatypes.generated.Uint32;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.http.HttpService;
import rx.Observable;
import rx.Subscription;

import java.io.IOException;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import static org.web3j.protocol.core.DefaultBlockParameterName.EARLIEST;
import static org.web3j.protocol.core.DefaultBlockParameterName.LATEST;

/**
 * @program: demo
 * @description:
 * @author: Wangzheng
 * @create: 2020-12-22 20:09
 **/
@Service
public class TransactionServiceImpl implements TransactionService {
	@Autowired
	private static Subscription subscription;
	@Autowired
	private static Web3j web3j;
	@Autowired
	private TransactionService transactionService;
	@Autowired
	private BlockService blockService;

	@Autowired
	private TransactionDao transactionDao;

	
	
	AtomicInteger atomicInteger;


	@Override
	public HashMap<String, Object> getTransactionDataToMap() {
		//先定义一个map
		HashMap<String, Object> map = new HashMap<>();
		//调用service拿到块的最大编号
		BigInteger bigBlockNum = blockService.findBigBlockNum();
		//从最初,到最近的块 遍历
		for (int i = 1; i <= bigBlockNum.intValue(); i++) {
			subscription = Web3j.build(new HttpService(Normal.SERVER)).replayTransactionsObservable(
					DefaultBlockParameter.valueOf(String.valueOf(EARLIEST)),
					DefaultBlockParameter.valueOf(String.valueOf(LATEST))).subscribe(transaction -> {

				Request<?, EthGetTransactionReceipt> ethGetTransactionReceiptRequest = Web3j.build(new HttpService(Normal.SERVER)).ethGetTransactionReceipt(transaction.getHash());
				EthGetTransactionReceipt send = null;
				try {
					send = ethGetTransactionReceiptRequest.send();
				} catch (IOException e) {
					e.printStackTrace();
				}
				//把初始的block数据写入map里面:
				BigInteger number = transaction.getBlockNumber();
				map.put("hash" + String.valueOf(number), transaction.getHash());
				map.put("nonce" + String.valueOf(number), transaction.getNonce());
				map.put("blockHash" + String.valueOf(number), transaction.getBlockHash());
				map.put("blockNumber" + String.valueOf(number), transaction.getBlockNumber());
				map.put("transactionIndex" + String.valueOf(number), transaction.getTransactionIndex());
				map.put("fromAdd" + String.valueOf(number), transaction.getFrom());
				map.put("toAdd" + String.valueOf(number), transaction.getTo());
				map.put("value" + String.valueOf(number), transaction.getValue());
				map.put("gasPrice" + String.valueOf(number), transaction.getGasPrice());
				map.put("gas" + String.valueOf(number), transaction.getGas());
				//通过得到交易凭证来获取每笔交易的gas使用量
				map.put("gasUsed" + String.valueOf(number), send.getResult().getGasUsed());
				/*map.put("input" + String.valueOf(number), transaction.getInput());
				map.put("creates" + String.valueOf(number), transaction.getCreates());
				map.put("publicKey" + String.valueOf(number), transaction.getPublicKey());
				map.put("raw" + String.valueOf(number), transaction.getRaw());
				map.put("r" + String.valueOf(number), transaction.getR());
				map.put("s" + String.valueOf(number), transaction.getS());
				map.put("v" + String.valueOf(number), transaction.getV());*/
			});
		}
		return map;
	}

	@Override
	public HashMap<Integer, Transaction> getTransactionDataToPojo() {
		//调用service拿到最大块的编号:
		BigInteger bigBlockNum = blockService.findBigBlockNum();
		//定义此方法要返回的map对象 key为0-最大块的编号 value为Block pojo对象
		HashMap<Integer, Transaction> map1 = new HashMap<>();
		//调用service方法拿数据块的map 即将通过此map完成对实体的封装:
		HashMap<String, Object> map = transactionService.getTransactionDataToMap();
		//循环遍历拿取map的key 同时new Block()对象把需要封装的对象添加进实体:
		for (int j = 1; j <= bigBlockNum.intValue(); j++) {

			map1.put(Integer.valueOf(j), new Transaction()
					.setHash((String) map.get("hash" + j))
					.setNonce((BigInteger) map.get("nonce" + j))
					.setBlockHash((String) map.get("blockHash" + j))
					.setBlockNumber((BigInteger) map.get("blockNumber" + j))
					.setTransactionIndex((BigInteger) map.get("transactionIndex" + j))
					.setFromAdd((String) map.get("fromAdd" + j))
					.setToAdd((String) map.get("to" + j))
					.setValue((BigInteger) map.get("value" + j))
					.setGasPrice((BigInteger) map.get("gasPrice" + j))
					.setGas((BigInteger) map.get("gas" + j))
					.setGasUsed((BigInteger) map.get("gasUsed" + j)));
		}
		return map1;

	}

	@Override
	public String insertTransactionData() {
		//调用service拿到我们封装好的带有Block对象的map
		HashMap<Integer, Transaction> map = transactionService.getTransactionDataToPojo();
		//循环遍历map对象,拿到block对象
		for (Map.Entry<Integer, Transaction> entry : map.entrySet()) {
			Transaction transaction = entry.getValue();
			//调用dao的方法,插入数据库
			transactionDao.insertTransaction(transaction);
		}
		return null;

	}


	@Override
	public void insertDB() {
		Web3j web3j = Web3j.build(new HttpService(Normal.SERVER));
		String status1 = "success";
		String status2 = "fail";
		Transaction transaction = new Transaction();
		List<BigInteger> list = new ArrayList<>();
		 AtomicInteger count = new AtomicInteger();
		
		Observable<USDT.TransferEventResponse> transferEventResponseObservable = new USDTapi().transferEventOb();
		transferEventResponseObservable.subscribe(tx -> {
			Event event = new Event("Transfer",
					Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}),
					Arrays.<TypeReference<?>>asList(new TypeReference<Bytes32>() {},
							new TypeReference<Uint32>() {}));
			EthFilter filter = new EthFilter(EARLIEST, LATEST, Normal.CONTRACTADDRESS);
			filter.addSingleTopic(EventEncoder.encode(event));
			list.add(tx.value);
		});

		 web3j.replayTransactionsObservable(
				DefaultBlockParameter.valueOf(String.valueOf(EARLIEST)),
				DefaultBlockParameter.valueOf(String.valueOf(LATEST))).subscribe(ta -> {
			Request<?, EthGetTransactionReceipt> ethGetTransactionReceiptRequest = web3j.ethGetTransactionReceipt(ta.getHash());
			EthGetTransactionReceipt send = null;
			try {
				send = ethGetTransactionReceiptRequest.send();
			} catch (IOException e) {
				e.printStackTrace();
			}
			 System.out.println(send.getTransactionReceipt().get().getBlockNumber());
		
			if (send.getTransactionReceipt().get().getStatus().contains("0x1")) {
				transaction.setStatus(status1);
			} else {
				transaction.setStatus(status2);
			}
			
			if (send.getResult().getLogs().isEmpty()) {
				transaction.setKind(0).setValue(ta.getValue());
			} else {
				transaction.setKind(1);
			}
			
			transaction
					.setHash(ta.getHash())
					.setNonce(ta.getNonce())
					.setBlockHash(ta.getBlockHash())
					.setBlockNumber(ta.getBlockNumber())
					.setTransactionIndex(ta.getTransactionIndex())
					.setFromAdd(ta.getFrom())
					.setToAdd(ta.getTo())
					.setValue(ta.getValue().intValue()==0?list.get(count.incrementAndGet()-1):ta.getValue())
					.setGasPrice(ta.getGasPrice())
					.setGas(ta.getGas())
					.setGasUsed(send.getTransactionReceipt().get().getGasUsed())
					.setContractAddress(send.getTransactionReceipt().get().getContractAddress() != null ? send.getTransactionReceipt().get().getContractAddress() : ta.getTo())
					.setGasLimit(send.getTransactionReceipt().get().getGasUsed().multiply(ta.getGasPrice()));
			transactionDao.insertTransaction(transaction);
			System.out.println(transaction);
		});

	}

	@Override
	public void insertDBByLastBlockNum(BigInteger num) {
		Web3j web3j = Web3j.build(new HttpService(Normal.SERVER));
		String status1 = "success";
		String status2 = "fail";
		List<BigInteger> list = new ArrayList<>();
		AtomicInteger count = new AtomicInteger();
		Transaction transaction = new Transaction();
		Observable<USDT.TransferEventResponse> transferEventResponseObservable = new USDTapi().transferEventOb();
		transferEventResponseObservable.subscribe(tx -> {
			Event event = new Event("Transfer",
					Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}),
					Arrays.<TypeReference<?>>asList(new TypeReference<Bytes32>() {},
							new TypeReference<Uint32>() {}));
			EthFilter filter = new EthFilter(LATEST, LATEST, Normal.CONTRACTADDRESS);
			filter.addSingleTopic(EventEncoder.encode(event));
			list.add(tx.value);
		});

		 web3j.replayTransactionsObservable(
				LATEST,
				LATEST).subscribe(ta -> {
			Request<?, EthGetTransactionReceipt> ethGetTransactionReceiptRequest = web3j.ethGetTransactionReceipt(ta.getHash());
			EthGetTransactionReceipt send = null;
			Event event = new Event("Transfer",
					Arrays.<TypeReference<?>>asList(new TypeReference<Address>() {}),
					Arrays.<TypeReference<?>>asList(new TypeReference<Bytes32>() {},
							new TypeReference<Uint32>() {}));
			EthFilter filter = new EthFilter(LATEST, LATEST, Normal.CONTRACTADDRESS);
			filter.addSingleTopic(EventEncoder.encode(event));
			try {
				send = ethGetTransactionReceiptRequest.send();
			} catch (IOException e) {
				e.printStackTrace();
			}

			
			if (send.getTransactionReceipt().get().getStatus().contains("0x1")) {
				transaction.setStatus(status1);
			} else {
				transaction.setStatus(status2);
			}

			if (send.getResult().getLogs().isEmpty()) {
				transaction.setKind(0).setValue(ta.getValue());
			} else {
				transaction.setKind(1);
			}

			transaction
					.setHash(ta.getHash())
					.setNonce(ta.getNonce())
					.setBlockHash(ta.getBlockHash())
					.setBlockNumber(ta.getBlockNumber())
					.setTransactionIndex(ta.getTransactionIndex())
					.setFromAdd(ta.getFrom())
					.setToAdd(ta.getTo())
					.setValue(ta.getValue().intValue()==0?list.get(count.incrementAndGet()-1):ta.getValue())
					.setGasPrice(ta.getGasPrice())
					.setGas(ta.getGas())
					.setGasUsed(send.getTransactionReceipt().get().getGasUsed())
					.setContractAddress(send.getTransactionReceipt().get().getContractAddress() != null ? send.getTransactionReceipt().get().getContractAddress() : ta.getTo())
					.setGasLimit(send.getTransactionReceipt().get().getGasUsed().multiply(ta.getGasPrice()));
			if (transaction.getBlockNumber().intValue() == num.intValue()){
				transactionDao.insertTransaction(transaction);
			}
			System.out.println(transaction);
			
		});

		
	}


}

















/*
* 
* 
* 
* 
* 
* 
* 
* 
* System.out.println("=========遍历旧交易 star ===========");
				System.out.println(" Hash " + " : " + tx.getHash());
			System.out.println(" Nonce " + " : " + tx.getNonce());
			System.out.println(" BlockHash " + " : " + tx.getBlockHash());
			System.out.println(" BlockNumber " + " : " + tx.getBlockNumber());
			System.out.println(" TransactionIndex " + " : " + tx.getTransactionIndex());
			System.out.println(" From " + " : " + tx.getFrom());
			System.out.println(" To " + " : " + tx.getTo());
			System.out.println(" Value " + " : " + tx.getValue());
			System.out.println(" GasPrice " + " : " + tx.getGasPrice());
			System.out.println(" Gas " + " : " + tx.getGas());
			System.out.println(" Input " + " : " + tx.getInput());
			System.out.println(" Creates " + " : " + tx.getCreates());
			System.out.println(" PublicKey " + " : " + tx.getPublicKey());
			System.out.println(" Raw " + " : " + tx.getRaw());
			System.out.println(" R " + " : " + tx.getR());
			System.out.println(" S " + " : " + tx.getS());
			System.out.println(" V " + " : " + tx.getV()); */